package com.riven.redisson.concurrent;

import com.riven.redisson.annotation.DistributedLock;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author riven
 * @since 2025-05-26
 */
@Slf4j
public class CustomizedProviderLockSubjectResolver extends BaseLockSubjectResolver {

    private static final NoOpLockSubjectProvider NO_OP = new NoOpLockSubjectProvider();
    private final Map<String, LockSubjectProvider> cachedProviders = new ConcurrentHashMap<>(32);

    @Override
    public Object doResolve(MethodSignature methodSignature, Object[] args) {
        final String methodUniqueId = methodSignature.toLongString();
        LockSubjectProvider lockSubjectProvider = cachedProviders.get(methodUniqueId);
        if (lockSubjectProvider == null) {
            Method method = methodSignature.getMethod();
            DistributedLock distributedLock = method.getDeclaredAnnotation(DistributedLock.class);
            Class<? extends LockSubjectProvider> providerClass = distributedLock.subjectProvider();
            LockSubjectProvider providerInstance = null;
            if (providerClass != NoOpLockSubjectProvider.class) {
                try {
                    providerInstance = providerClass.getDeclaredConstructor().newInstance();
                } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
                    log.warn("create new instance error for class " + providerClass.getSimpleName(), e);
                }
            }
            providerInstance = providerInstance == null ? NO_OP : providerInstance;
            cachedProviders.put(methodUniqueId, providerInstance);
            lockSubjectProvider = providerInstance;
        }
        Object resolvedSubject = lockSubjectProvider.getSubject();
        if (lockSubjectProvider != NO_OP && resolvedSubject == null) {
            //resolved nothing, don't lock
            return DO_NOT_LOCK;
        }
        return resolvedSubject;
    }
}
